# frozen_string_literal: true

require 'spec_helper'

RSpec.describe VulnerabilityPresenter, feature_category: :vulnerability_management do
  let_it_be(:project) { create(:project) }
  let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project) }
  let_it_be(:sast_scan) do
    { type: 'sast', status: 'success', start_time: 'placeholder', end_time: 'placeholder' }
  end

  let(:finding) { create(:vulnerabilities_finding, :with_secret_detection, :with_pipeline, project: project, description: 'Finding Description') }

  let_it_be(:finding2) { create(:vulnerabilities_finding, :with_secret_detection, scan: sast_scan) }

  let_it_be(:vulnerability_links) do
    [
      { name: 'link1', url: 'www.example.com/link1' },
      { name: 'link2', url: 'www.exampple.com/link2' }
    ]
  end

  subject  { described_class.new(finding.vulnerability) }

  describe '#links' do
    context 'when vulnerability has links' do
      before do
        allow(finding.vulnerability).to receive(:links).and_return(vulnerability_links)
      end

      it 'expect links to be instance of active support hash with indifferent access' do
        expect(subject.links).to all(be_a(ActiveSupport::HashWithIndifferentAccess))
      end
    end
  end

  describe '#scanner' do
    it 'returns the scanner for a finding' do
      expect(subject.scanner).to eql(finding.scanner)
    end

    it 'returns empty hash if scanner is missing for a vulnerabilities_finding' do
      finding2.scanner = nil

      expect(described_class.new(finding2.vulnerability).scanner).to eql({})
    end
  end

  describe '#scan' do
    it 'returns the scan for a finding' do
      expect(described_class.new(finding2.vulnerability).scan).to eql(finding2.scan)
    end

    it 'returns empty hash if scan is missing for a vulnerabilities_finding' do
      finding2.scan = nil

      expect(described_class.new(finding2.vulnerability).scan).to eql({})
    end
  end

  describe '#remediations' do
    let(:finding) { create(:vulnerabilities_finding, :with_secret_detection, :with_remediation, :with_pipeline, project: project) }

    it 'returns remediations' do
      expect(subject.remediations.count).to be(1)
    end
  end

  describe '#location_text' do
    context 'when line is nil' do
      it 'returns a string with file' do
        allow(subject).to receive(:line).and_return(nil)

        expect(subject.location_text).to eq finding.file
      end
    end

    context 'when line is present' do
      it 'returns a string with file and line' do
        expect(subject.location_text).to eq "#{finding.file}:#{finding.location['start_line']}"
      end
    end
  end

  describe '#location_link_with_raw_path' do
    context 'when the end_line is the same as the start_line' do
      it 'returns the location link in raw format' do
        path = subject.location_link_with_raw_path

        expect(path).to include('raw')
        expect(path).to include(finding.file)
        expect(path).to end_with("#L#{finding.location['start_line']}")
      end
    end

    context 'when the end_line is nil' do
      before do
        finding.location['end_line'] = nil
        finding.save!
      end

      it 'returns the location link in raw format' do
        path = subject.location_link_with_raw_path

        expect(path).to end_with("#L#{finding.location['start_line']}")
      end
    end

    context 'when the end_line is different from the start_line' do
      before do
        finding.location['end_line'] = finding.location['start_line'] + 1
        finding.save!
      end

      it 'returns the location link in raw format' do
        path = subject.location_link_with_raw_path

        expect(path).to end_with("#L#{finding.location['start_line']}-#{finding.location['end_line']}")
      end
    end
  end

  describe '#location_link' do
    it 'returns the location link in blob format' do
      path = subject.location_link

      expect(path).to include('blob')
      expect(path).to include(finding.file)
      expect(path).to end_with("#L#{finding.location['start_line']}")
    end
  end

  describe '#blob_path' do
    it 'returns the path in blob format' do
      path = subject.blob_path

      expect(path).to include('blob')
      expect(path).to include(finding.file)
      expect(path).to end_with("#L#{finding.location['start_line']}")
    end

    it 'returns nil if file is missing in the finding' do
      allow(subject).to receive(:file).and_return(nil)

      expect(subject.blob_path).to be(nil)
    end
  end

  describe '#raw_path' do
    it 'returns the path in raw format' do
      path = subject.raw_path

      expect(path).to include('raw')
      expect(path).to include(finding.file)
      expect(path).to end_with("#L#{finding.location['start_line']}")
    end

    it 'returns nil if file is missing in the finding' do
      allow(subject).to receive(:file).and_return(nil)

      expect(subject.raw_path).to be(nil)
    end
  end

  describe '#jira_issue_description' do
    let(:expected_jira_issue_description) do
      <<-JIRA.strip_heredoc
        Issue created from vulnerability [#{finding.vulnerability.id}|http://localhost/#{project.full_path}/-/security/vulnerabilities/#{finding.vulnerability.id}]

        h3. Description:

        Description of #{finding.vulnerability.title}

        * Severity: high
        * Location: [aws-key.py:5|http://localhost/#{project.full_path}/-/blob/#{finding.location.dig('commit', 'sha')}/aws-key.py#L5]




        h3. Scanner:

        * Name: Find Security Bugs
      JIRA
    end

    it 'returns the jira description in string format' do
      jira_issue_description = subject.jira_issue_description

      expect(jira_issue_description).to eq(expected_jira_issue_description)
    end
  end

  describe '#description' do
    let(:vulnerability) { finding.vulnerability }

    context 'when the vulnerability description field is populated' do
      it 'returns the description for the vulnerability' do
        expect(subject.description).to eq(vulnerability.description)
      end
    end

    context 'when the vulnerability description field is empty' do
      before do
        vulnerability.description = nil
        vulnerability.save!
      end

      it 'returns the description for the vulnerability finding' do
        expect(subject.description).not_to eq(vulnerability.description)
        expect(subject.description).to eq(finding.description)
      end
    end
  end
end
